package tcp_app;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import java.io.*;

//Java Tcp_app :  Server
public class Server {
	//主程序开始的地方
	public static void main(String[] args) throws Exception{
		new Oper();		//通过Oper类的构造方法开始
	}
	
}

//Oper类, SocketServer服务器运行的类
class Oper{
	public static boolean flag = false;			//定义服务器的启动按钮, 初始化为未启动
	public static int count = -1;				//定义客户端数量(以数组下标表示个数)
	ServerLogin sl;								//定义登录窗口对象
	ServerChatWindows scw;						//定义聊天窗口对象
    ServerSocket server;						//服务器
	Socket client;								//客户端									
	public Oper() throws Exception{				//Oper类构造方法
		sl = new ServerLogin();									//创建显示登录窗口,根据用户操作选择是否启动服务器
		while(!this.flag) {System.out.println("");}				//用户若无操作, 程序进入死循环等待状态
		sl.jf.dispose();
		scw = new ServerChatWindows();							//关闭登录窗口, 产生聊天窗口
		server = new ServerSocket(8888);						//建立服务器, 指定端口号为 8888
		//进入死循环, 不断接受客户端的连接, 利用多线程实现多个客户端的连接
		while(true) {
			client = server.accept();							//等待客户端连接
			new Thread(new ServerThread(client, scw)).start();	//开辟服务器对此客户端的一个线程,将此客户端和服务器聊天界面传入,并开始运行此线程
			count++;											//客户端数量累加
		}
	}
}


//服务器对应客户端的多线程类, 客户端连接的数量就是服务器线程启动的数量
class ServerThread implements Runnable{
	Socket client;								//定义客户端
	ServerChatWindows scw;						//服务器聊天窗口
	JTextField jtf_news;						//定义发送信息的文本框
	JTextArea jta_mess;							//定义不可编辑的文本区域作为聊天记录界面
	JButton jbt_send;							//定义发送按钮
	PrintStream out;							//定义客户端输出流
	BufferedReader buf;							//定义客户端输入流
	public static String str;					//定义全局变量 str 为接收客户端发送的信息
	//此类的构造方法, 接收客户端 和 服务器的固定聊天界面
	public ServerThread(Socket client, ServerChatWindows scw) {
		this.client = client;
		this.scw = scw;
		this.jtf_news = scw.jtf_news;
		this.jbt_send = scw.jbt_send;
		this.jta_mess = scw.jta_mess;			//进行对象的复制传输
	}
	//线程的run()方法
	public void run() {
		try {
			buf = new BufferedReader(new InputStreamReader(client.getInputStream()));	//得到此客户端的输入流
			out = new PrintStream(client.getOutputStream());							//得到此客户端的输出流
			this.jbt_send.addActionListener(new ServerSendNews(this.out, this.scw));	//增加服务器端信息发送的按钮监听器
			this.jtf_news.addKeyListener(new ServerSendNews(this.out, this.scw));		//增加服务器端信息发送的键盘监听器,回车键为发送
			//进入死循环, 不断得到客户端发送的信息
			while(true) {
				this.str = null;					//初始化信息为 null
				str = buf.readLine();				//得到客户端发送的信息
				this.jbt_send.doClick();			//模拟点击服务器信息发送的按钮, 将客户端发送的信息回显给所有客户端
			}
		}catch(Exception e) {						//当客户端断开连接后或其它原因的异常处理提醒
			System.out.println("Client isn't connected!");
		}
	}
}


//服务器启动界面窗口
class ServerLogin{
	JFrame jf;								//定义窗口
	Container con;							//存放组件的容器
	JPanel jpl_jlb;							
	JPanel jpl_jbt; 						//定义两个存放组件的二级容器
	JLabel jlb;								//定义服务器信息标签
	JButton jbt_on;							
	JButton jbt_ce;							//定义两个按钮,作为登录和取消键
	//通过构造方法生成聊天界面窗口
	public ServerLogin() throws Exception {
		jf = new JFrame("Chat-Room Server");		//登录窗口窗口上栏信息
		con = jf.getContentPane();
		jpl_jlb = new JPanel();
		jpl_jbt = new JPanel();
		
		jlb = new JLabel("Chat--Server");			//标签信息
		jbt_on = new JButton("On");					//登录按钮
		jbt_ce = new JButton("Off");				//取消按钮
		
		//为登录按纽增加事件监听器, 当用户点击 on 时, Oper类全局变量 flag变为 ture, 表示服务器启动, Oper类启动服务器
		jbt_on.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				Oper.flag = true;
			}
		});
		
		//为取消按钮增加事件监听器, 当用户点击 off 时退出程序
		jbt_ce.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		});
		
		//向容器增加组件
		
		jpl_jlb.add(jlb);
		jpl_jbt.add(jbt_on);
		jpl_jbt.add(jbt_ce);
		
		con.add(jpl_jlb, BorderLayout.NORTH);
		con.add(jpl_jbt, BorderLayout.SOUTH);
		
		//设置窗体属性
		jf.setSize(300, 120);			//设置大小
		jf.setLocation(600, 260);		//设置位置
		jf.setVisible(true);			//设置可见
		jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);			//设置可关闭
	}
}


//服务器聊天界面窗口类
class ServerChatWindows{
	JFrame jf;							//定义窗口
	Container con;						//定义容器
	JPanel send;
	JPanel mess;						//二级容器
	JTextField jtf_news;
	JTextArea jta_mess;
	JScrollPane jsp;
	JButton jbt_send;					//文本域 , 文本框, 按钮等组件
	BufferedReader buf;					//定义输入流
	PrintStream out;					//定义输出流
	//通过构造方法生成 服务器聊天界面
	public ServerChatWindows() throws Exception{
		jf = new JFrame("Server");
		con = jf.getContentPane();
		jtf_news = new JTextField("Hello, I'm Server!", 14);
		jta_mess = new JTextArea(15, 25);
        jta_mess.setLineWrap(true);
		jsp = new JScrollPane(jta_mess, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

		jta_mess.setEditable(false);		//多行文本域设置为不可编辑, 用来存放聊天记录
		jbt_send = new JButton("send");
		
		mess = new JPanel();
		send = new JPanel();
		
		send.add(jtf_news);
		send.add(jbt_send);
		
		con.add(jsp, BorderLayout.CENTER);
		con.add(send, BorderLayout.SOUTH);
		
		jf.pack();
//		jf.setSize(280, 340);
		jf.setLocation(500, 260);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);		//如上, 设置聊天窗口的属性
		
	}
}


//定义按钮及键盘监听器, 监听服务器发送给的内容
class ServerSendNews implements ActionListener, KeyListener{
	private static int count = -1;		//定义发送信息的次数
	private ServerChatWindows scw;		//定义聊天服务器窗口
	private JTextArea jta_mess;			//定义存放聊天记录的文本框
	private JTextField jtf_news;		//信息发送的文本框
	private JButton jbt_send;			//发送按钮
	private PrintStream out;			//得到向客户端的输出流
	private boolean flag;				//判断数据流为回显客户端的信息或者为服务器发送的信息
	private String str;					//存储信息的字符串
	//构造方法, 得到客户端的输出流和服务器的聊天窗口
	public ServerSendNews(PrintStream out, ServerChatWindows scw) throws Exception{
		this.out = out;
		this.jtf_news = scw.jtf_news;
		this.jta_mess = scw.jta_mess;
		this.jbt_send = scw.jbt_send;			//对象的复制传输
	}
	//按钮发生动作需执行的事件
	public void actionPerformed(ActionEvent e) {
		this.flag = true;					//初始化flag为true, 即服务器发送的信息
		//若ServerThread类的str属性信息不为空, 则此动作为 模拟点击按钮动作, 即回显客户端信息, flag变为 false, 得到客户端信息
		if(ServerThread.str != null){
			this.str = ServerThread.str;
			this.flag = false;
		}
		else this.str = "Server: " + jtf_news.getText();		//否则,则为服务器所发送的信息
		out.println(this.str);									//输出此信息至其它客户端
		this.count++;											//输出信息次数累加
		if((this.count+1) % (Oper.count+1) == 0) {				//若输出信息次数可以整除客户端数量, 服务器的信息文本域也将变动, 消息记录也累加
			if(this.flag == true) jtf_news.setText("");			//若为服务器发送的信息, 则清空服务器的信息发送文本域
			this.jta_mess.append(this.str + "\n");				//累加消息记录
		}
	}
	//增加键盘监听器事件, 当用户输入信息时输入回车键, 执行此事件
	//同上, 此为服务器所发送信息, 无需判断信息流
	public void keyPressed(KeyEvent arg0) {
		if(arg0.getKeyCode() == KeyEvent.VK_ENTER){
			this.str = "Server: " + jtf_news.getText();
			out.println(this.str);
			this.count++;
			if((this.count+1) % (Oper.count+1) == 0) {
				jtf_news.setText("");
				this.jta_mess.append(this.str + "\n");
			}
		}
	}
	public void keyReleased(KeyEvent arg0) {}
	public void keyTyped(KeyEvent arg0) {}
}
